package org.campuscontent.ui;

import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.util.Locale;
import java.util.ResourceBundle;
import java.util.Vector;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBoxMenuItem;
import javax.swing.JOptionPane;
import javax.swing.JPopupMenu;
import javax.swing.JToolBar;

import org.campuscontent.config.languages.LanguageListener;
import org.campuscontent.config.languages.LanguageManager;
import org.campuscontent.config.languages.LanguageManager.Entry;
import org.campuscontent.ui.layout.LayoutConstants;
import org.campuscontent.ui.layout.LayoutManager;
import org.campuscontent.ui.multipanel.UsersPanelList;
import org.jgraph.graph.DefaultEdge;
import org.jgraph.graph.Port;
import org.jgraph.graph.PortView;

public class AutomatToolbar extends JToolBar implements MouseListener, LanguageListener, ActionListener
{
    
	public AutomatToolbar()
    {
        initBar();
        initListeners();
        
        setVisibleUsersPanel( false );
    }
	
	public void update( ResourceBundle resources )
	{
        jbNodeAdd.setToolTipText( LanguageManager.str( this, "toolbar.add_node" ) );
        jbNodeDel.setToolTipText( LanguageManager.str( this, "toolbar.del_node" ) );
        jbEdgeAdd.setToolTipText( LanguageManager.str( this, "toolbar.add_edge" ) );
        jbEdgeDel.setToolTipText( LanguageManager.str( this, "toolbar.del_edge" ) );
        jbUndo   .setToolTipText( LanguageManager.str( this, "toolbar.undo"     ) );
        jbRedo   .setToolTipText( LanguageManager.str( this, "toolbar.redo"     ) );
        jbZoomIn .setToolTipText( LanguageManager.str( this, "toolbar.zoom_in"  ) );
        jbZoomOut.setToolTipText( LanguageManager.str( this, "toolbar.zoom_out" ) );
        jbZoom100.setToolTipText( LanguageManager.str( this, "toolbar.zoom_100" ) );
        
        jbPanels  .setToolTipText( LanguageManager.str( this, "toolbar.panels"   ) );
        jbLanguage.setToolTipText( LanguageManager.str( this, "toolbar.language" ) );
	}

    @SuppressWarnings("unchecked")
    private void initBar()
    {
    	Class cl = getClass();
        
    	jbArray = new JButton[ 11 ];

        ImageIcon nodeAdd  = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/node_add.png"  ) );
        ImageIcon nodeDel  = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/node_del.png"  ) );
        ImageIcon edgeAdd  = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/edge_add.png"  ) );
        ImageIcon edgeDel  = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/edge_del.png"  ) );
        ImageIcon undo     = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/undo.png"      ) );
        ImageIcon redo     = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/redo.png"      ) );
        ImageIcon zoomIn   = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/zoom_+.png"    ) );
        ImageIcon zoomOut  = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/zoom_-.png"    ) );
        ImageIcon zoom100  = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/zoom_1_1.png"  ) );
        ImageIcon panels   = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/panels.png"    ) );
        ImageIcon language = new ImageIcon( cl.getResource( "/org/campuscontent/images/toolbar/language.png"  ) );
        
        jbNodeAdd  = new JButton( nodeAdd  );
        jbNodeDel  = new JButton( nodeDel  );
        jbEdgeAdd  = new JButton( edgeAdd  );
        jbEdgeDel  = new JButton( edgeDel  );
        jbUndo     = new JButton( undo     );
        jbRedo     = new JButton( redo     );
        jbZoomIn   = new JButton( zoomIn   );
        jbZoomOut  = new JButton( zoomOut  );
        jbZoom100  = new JButton( zoom100  );
        jbPanels   = new JButton( panels   );
        jbLanguage = new JButton( language );
        
        showBorders( false );

        jbNodeAdd .setFocusPainted( false );
        jbNodeDel .setFocusPainted( false );
        jbEdgeAdd .setFocusPainted( false );
        jbEdgeDel .setFocusPainted( false );
        jbUndo    .setFocusPainted( false );
        jbRedo    .setFocusPainted( false );
        jbZoomIn  .setFocusPainted( false );
        jbZoomOut .setFocusPainted( false );
        jbZoom100 .setFocusPainted( false );
        jbPanels  .setFocusPainted( false );
        jbLanguage.setFocusPainted( false );

        jbArray[ 0  ] = jbNodeAdd;
        jbArray[ 1  ] = jbNodeDel;
        jbArray[ 2  ] = jbEdgeAdd;
        jbArray[ 3  ] = jbEdgeDel;
        jbArray[ 4  ] = jbUndo;
        jbArray[ 5  ] = jbRedo;
        jbArray[ 6  ] = jbZoomIn;
        jbArray[ 7  ] = jbZoomOut;
        jbArray[ 8  ] = jbZoom100;
        jbArray[ 9  ] = jbPanels;
        jbArray[ 10 ] = jbLanguage;
        
        jbNodeDel.setEnabled( false );
        jbEdgeDel.setEnabled( false );
        jbUndo.setEnabled( false );
        jbRedo.setEnabled( false );

        jbNodeAdd .setToolTipText( LanguageManager.str( this, "toolbar.add_node" ) );
        jbNodeDel .setToolTipText( LanguageManager.str( this, "toolbar.del_node" ) );
        jbEdgeAdd .setToolTipText( LanguageManager.str( this, "toolbar.add_edge" ) );
        jbEdgeDel .setToolTipText( LanguageManager.str( this, "toolbar.del_edge" ) );
        jbUndo    .setToolTipText( LanguageManager.str( this, "toolbar.undo"     ) );
        jbRedo    .setToolTipText( LanguageManager.str( this, "toolbar.redo"     ) );
        jbZoomIn  .setToolTipText( LanguageManager.str( this, "toolbar.zoom_in"  ) );
        jbZoomOut .setToolTipText( LanguageManager.str( this, "toolbar.zoom_out" ) );
        jbZoom100 .setToolTipText( LanguageManager.str( this, "toolbar.zoom_100" ) );
        jbPanels  .setToolTipText( LanguageManager.str( this, "toolbar.panels"   ) );
        jbLanguage.setToolTipText( LanguageManager.str( this, "toolbar.language" ) );
        
        add( jbNodeAdd );
        add( jbNodeDel );
        add( jbEdgeAdd );
        add( jbEdgeDel );
        add( jbUndo    );
        add( jbRedo    );
        addSeparator( new Dimension( 20, 20 ) );
        add( jbZoomIn  );
        add( jbZoomOut);
        add( jbZoom100 );
        addSeparator( new Dimension( 40, 20 ) );
        add( jbPanels  );
        add( jbLanguage );
        
        zoom = new ZoomManager();
        insertionPoint = new Point2D.Double( 10, 10 );
    } 
    
    public void setVisibleUsersPanel( boolean status )
    {
        jbPanels.setVisible( status );
    }

    protected void initListeners()
    {
        LanguageManager lm = LanguageManager.getInstance();
        lm.addListener( this );
        
     	jbNodeAdd.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    graph.insert( new Point2D.Double( defaultX, defaultY ) );
                    defaultX += offsetXInsertNode;
                    defaultY += offsetYInsertNode;
                }
            }
        } );

        jbNodeDel.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                Object[] selection = UIUtils.getFullCellsOfCurrentSelection( graph );
                if ( selection != null && selection.length != 0 )
                {
                    graph.getModel().remove( selection );
                }
            }
        } );
        
        jbEdgeAdd.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                marqueeHandler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                
                if ( graph != null && !graph.isSelectionEmpty() )
                {
                    Object[] cells = graph.getSelectionCells();
                    
                    if ( cells.length == 2 )
                    {
                        PortView source = graph.getDefaultPortForCell( cells[ 0 ] );
                        PortView target = graph.getDefaultPortForCell( cells[ 1 ] );
                        
                        marqueeHandler.connect( (Port)source.getCell(), (Port)target.getCell() );
                    }
                }
            }
        } );
        
        jbEdgeDel.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                marqueeHandler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                
                if ( graph != null && !graph.isSelectionEmpty() )
                {
                    Object[] cells = graph.getSelectionCells();
                    
                    for ( Object cc : cells )
                    {
                        if ( cc instanceof DefaultEdge )
                        {
                            Object[] subcells = graph.getDescendants( new Object[]{ cc } );
                            graph.getModel().remove( subcells );
                        }
                    }
                }
            }
        } );
        
        jbZoomIn.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    handleZoom(  LayoutConstants.View.ZOOM_IN );
                }
            }
        } );
        
        jbZoomOut.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    handleZoom(  LayoutConstants.View.ZOOM_OUT ); 
                }
            }
        } );
        
        jbZoom100.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    handleZoom(  LayoutConstants.View.REAL );
                }
            }
        } );
                
        jbUndo.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    AutomataMarqueeHandler handler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                    handler.undo();
                }
            }
        } );
        
        jbRedo.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    AutomataMarqueeHandler handler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                    handler.redo();
                }
            }
        } );
        
        jbPanels.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                handleChangePanels(); 
            }
        } );
        
        jbLanguage.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                handleChangeLanguage(); 
            }
        } );
        
        // -------------------------------------------------

        jbNodeAdd .addMouseListener( this );
        jbNodeDel .addMouseListener( this );
        jbEdgeAdd .addMouseListener( this );
        jbEdgeDel .addMouseListener( this );
        jbUndo    .addMouseListener( this );
        jbRedo    .addMouseListener( this );
        jbZoomIn  .addMouseListener( this );
        jbZoomOut .addMouseListener( this );
        jbZoom100 .addMouseListener( this );
        jbPanels  .addMouseListener( this );
        jbLanguage.addMouseListener( this );
    }
    
    public void removeAllActionListener()
    {
    	for(int i = 0; i < jbArray.length; i++)
    	{
    		ActionListener[] listener	=	jbArray[i].getActionListeners();
    		
    		for(int j = 0; j < listener.length; j++)
    		{
    			jbArray[i].removeActionListener(listener[j]);
    		}
    	}
    }

    public void actionPerformed( ActionEvent e )
    {
        Object source = e.getSource();
        if ( source != null )
        {
            JCheckLanguage selected = (JCheckLanguage)source;
            LanguageManager lm = LanguageManager.getInstance();

            lm.set( selected.entry.instance );
            lm.refresh();
        }
    }
    
    public void handleChangePanels()
    {
        JPopupMenu menu = new JPopupMenu();
        menu.insert( new UsersPanelList(), 0 );
        menu.show( this, jbPanels.getX() + 10 , jbLanguage.getY() + 10 );
    }
    
    public void handleChangeLanguage()
    {
        LanguageManager lm = LanguageManager.getInstance();
        
        Vector<Entry> availableLg = lm.availableLanguages();
        
        if ( availableLg != null && !availableLg.isEmpty() )
        {
            Locale l = lm.currentLocale();
            JPopupMenu jp = new JPopupMenu();
            for ( Entry curr : availableLg )
            {
                JCheckLanguage opt = new JCheckLanguage( curr.label, l.getISO3Language().equals( curr.instance.getISO3Language() ), curr );
                opt.addActionListener( this );
                jp.add( opt );
            }
            
            jp.show( this, jbLanguage.getX() + 10 , jbLanguage.getY() + 10 );
        }
    }
    
    public void handleZoom( String type  )
    {
        try
        {
            String command = type;
            LayoutManager lm = LayoutManager.getInstance();

            if ( command.equals( LayoutConstants.View.FIT_IN_WINDOW ) )
            {
                lm.fitViewport( graph );
                graph.requestFocus();
                
                return;
            }
            else if ( command.equals( LayoutConstants.View.ZOOM_IN ) )
            {
                zoom.zoomIn();
            }
            else if ( command.equals( LayoutConstants.View.ZOOM_OUT ) )
            {
                zoom.zoomOut();
            }
            else if ( command.equals( LayoutConstants.View.REAL ) )
            {
                zoom.reset();
            }
            
            graph.setScale( zoom.current() );
            
            updateZoom( zoom.canZoomOut(), zoom.canZoomIn() );
        }
        catch ( Exception et )
        {
            JOptionPane.showMessageDialog( graph, et.getMessage(), "Error", JOptionPane.ERROR_MESSAGE );
        }
    }
    
    public void mouseClicked( MouseEvent e )
    {
    }
    
    public void updateListeners()
    {
     	jbNodeAdd.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    graph.insert( new Point2D.Double( defaultX, defaultY ) );
                    defaultX += offsetXInsertNode;
                    defaultY += offsetYInsertNode;
                }
            }
        } );

        jbNodeDel.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                Object[] selection = UIUtils.getFullCellsOfCurrentSelection( graph );
                if ( selection != null && selection.length != 0 )
                {
                    graph.getModel().remove( selection );
                }
            }
        } );
        
        jbEdgeAdd.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                marqueeHandler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                
                if ( graph != null && !graph.isSelectionEmpty() )
                {
                    Object[] cells = graph.getSelectionCells();
                    
                    if ( cells.length == 2 )
                    {
                        PortView source = graph.getDefaultPortForCell( cells[ 0 ] );
                        PortView target = graph.getDefaultPortForCell( cells[ 1 ] );
                        
                        marqueeHandler.connect( (Port)source.getCell(), (Port)target.getCell() );
                    }
                }
            }
        } );
        
        jbEdgeDel.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                marqueeHandler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                
                if ( graph != null && !graph.isSelectionEmpty() )
                {
                    Object[] cells = graph.getSelectionCells();
                    
                    for ( Object cc : cells )
                    {
                        if ( cc instanceof DefaultEdge )
                        {
                            Object[] subcells = graph.getDescendants( new Object[]{ cc } );
                            graph.getModel().remove( subcells );
                        }
                    }
                }
            }
        } );
        
        jbZoomIn.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    handleZoom(  LayoutConstants.View.ZOOM_IN );
                }
            }
        } );
        
        jbZoomOut.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    handleZoom(  LayoutConstants.View.ZOOM_OUT ); 
                }
            }
        } );
        
        jbZoom100.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    handleZoom(  LayoutConstants.View.REAL );
                }
            }
        } );
                
        jbUndo.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    AutomataMarqueeHandler handler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                    handler.undo();
                }
            }
        } );
        
        jbRedo.addActionListener( new ActionListener()
        {
            public void actionPerformed( ActionEvent ae )
            {
                if ( graph != null )
                {
                    AutomataMarqueeHandler handler = (AutomataMarqueeHandler)graph.getMarqueeHandler();
                    handler.redo();
                }
            }
        } );
    }

    public void mouseEntered( MouseEvent e )
    {
        if ( e.getSource() == jbNodeAdd  ) jbNodeAdd .setBorderPainted( true );
        if ( e.getSource() == jbNodeDel  ) jbNodeDel .setBorderPainted( true );
        if ( e.getSource() == jbEdgeAdd  ) jbEdgeAdd .setBorderPainted( true );
        if ( e.getSource() == jbEdgeDel  ) jbEdgeDel .setBorderPainted( true );
        if ( e.getSource() == jbUndo     ) jbUndo    .setBorderPainted( true );
        if ( e.getSource() == jbRedo     ) jbRedo    .setBorderPainted( true );
        if ( e.getSource() == jbZoomIn   ) jbZoomIn  .setBorderPainted( true );
        if ( e.getSource() == jbZoomOut  ) jbZoomOut .setBorderPainted( true );
        if ( e.getSource() == jbZoom100  ) jbZoom100 .setBorderPainted( true );
        if ( e.getSource() == jbPanels   ) jbPanels  .setBorderPainted( true );
        if ( e.getSource() == jbLanguage ) jbLanguage.setBorderPainted( true );
    }

    public void mouseExited( MouseEvent e )
    {
        showBorders( false );
    }

    public void mousePressed( MouseEvent e )
    {
    }

    public void mouseReleased( MouseEvent e )
    {
    }
    
    public void setGraph( AutomataGraph ag )
    {
        graph = ag;
    }

    private void showBorders( boolean state )
    {
        jbNodeAdd .setBorderPainted( state );
        jbNodeDel .setBorderPainted( state );
        jbEdgeAdd .setBorderPainted( state );
        jbEdgeDel .setBorderPainted( state );
        jbUndo    .setBorderPainted( state );
        jbRedo    .setBorderPainted( state );
        jbZoomIn  .setBorderPainted( state );
        jbZoomOut .setBorderPainted( state );
        jbZoom100 .setBorderPainted( state );
        jbPanels  .setBorderPainted( state );
        jbLanguage.setBorderPainted( state );
    }
    
    public void updateDeleteLinks( boolean state )
    {
        jbEdgeDel.setEnabled( state );
    }
    
    public void updateDeleteNodes( boolean state )
    {
        jbNodeDel.setEnabled( state );
    }
    
    public void updateUndoRedo( boolean undo, boolean redo )
    {
        jbUndo.setEnabled( undo );
        jbRedo.setEnabled( redo );
    }
    
    public void updateZoom( boolean out, boolean in )
    {
        jbZoomOut.setEnabled( out );
        jbZoomIn .setEnabled( in  );
    }
    
    public String toString()
    {
        return "TODO: at toolbar";
    }
    
    protected class JCheckLanguage extends JCheckBoxMenuItem
    {
        public JCheckLanguage( String lbl, boolean state, Entry obj )
        {
            super( lbl, state );
            entry = obj;
        }
        
        Entry entry;
    }

    public JButton jbNodeAdd;
    public JButton jbNodeDel;
    public JButton jbEdgeAdd;
    public JButton jbEdgeDel;
    public JButton jbUndo   ;
    public JButton jbRedo   ;
    public JButton jbZoomIn ;
    public JButton jbZoomOut;
    public JButton jbZoom100;
    
    public JButton jbPanels  ;
    public JButton jbLanguage;
    
    JButton[] jbArray; 
    
    protected Point2D insertionPoint;
    
    public AutomataGraph graph;
    private ZoomManager  zoom;
    
    private AutomataMarqueeHandler marqueeHandler;
    
    private static int defaultX = 10;
    private static int defaultY = 10;
    
    private static final int offsetXInsertNode = 10;
    private static final int offsetYInsertNode = 10;
}
